package org.duomn.xmltool;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.duomn.xmltool.ToXml.RefType;
import org.duomn.xmltool.transfer.Transfer;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

public class XMLUtils {
	
	private static Map<Class<? extends Transfer>, Transfer> _map = new HashMap<Class<? extends Transfer>, Transfer>();
	
	public static Element toXmlElement(Object obj) {
		Class<?> clazz = obj.getClass();
		
		ToXml objTX = clazz.getAnnotation(ToXml.class);
		if (objTX == null) return null;
		
		String objName = objTX.value();
		objName = objName.equals("") ? clazz.getSimpleName() : objName;
		Element ele = new Element(objName);
		
		Field[] fields = clazz.getDeclaredFields();
		try {
			for (Field f : fields) {
				f.setAccessible(true);
				ToXml fieldTX = f.getAnnotation(ToXml.class);
				if (fieldTX == null) continue;
				
				String asChild = fieldTX.asChild(); // 1.作为子节点名称 
				String attrName = fieldTX.value(); // 2.节点的属性名称
				boolean direct = attrName.equals(""); // direct作为直接使用，集合时，不使用额外的父标签
				attrName = direct ? f.getName() : attrName;
				RefType ref = fieldTX.type(); // 3.节点的类型
				Class<? extends Transfer> transClass = fieldTX.tranzfer(); // 4.获取字段转化器
				Transfer transfer = getTransfer(transClass);
				
				if (ref == RefType.BASE) {  // 类中属性
					Object valueObj = f.get(obj);
					if (valueObj == null) continue;
					String value = transfer.format(valueObj); // 对属性格式进行转化
					setBaseTypeField(ele, objName, f, asChild, attrName, value);
				} else if (ref == RefType.BASE_COLLECTION) { // 基础的集合类型
					Collection<?> c = (Collection<?>) f.get(obj);
					if (c == null) continue;
					Iterator<?> it = c.iterator();
					StringBuffer buf = new StringBuffer();
					while (it.hasNext()) {
						buf.append(transfer.format(it.next())).append(","); // 对属性格式进行转化
					}
					if (buf.length() > 1) buf.delete(buf.length() - 1, buf.length());
					setBaseTypeField(ele, objName, f, asChild, attrName, buf.toString());
				} else if (ref == RefType.CUSTOM) { // 自定义的类
					Object childObj = f.get(obj);
					if (childObj == null) continue;
					Element child = toXmlElement(childObj);
					ele.addContent(child);
				} else if (ref == RefType.CUSTOM_COLLECTION) { // 自定义的类的集合
					Collection<?> c = (Collection<?>) f.get(obj);
					if (c == null) continue;
					
					Element temp = null;
					if (direct) { // 直接添加到父节点中
						temp = ele;
					} else {
						Element child = ele.getChild(attrName); 
						if (child == null) {
							child = new Element(attrName);
							ele.addContent(child);
						}
						temp = child;
					}
					Iterator<?> it = c.iterator();
					while (it.hasNext()) {
						Element e = toXmlElement(it.next());
						temp.addContent(e);
					}
					if (!direct && temp.getChildren().size() == 0) temp.detach(); // 为空时，不添加该节点
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		}
		
		return ele;
	}
	
	/** 把RefType为Base的转化成xml */
	private static void setBaseTypeField(Element parent, Object obj, Field f, 
			String asChild, String attr, String value) throws IllegalArgumentException, IllegalAccessException {
		if (asChild.equals("")) { // 类的属性
			parent.setAttribute(attr, value);
		} else { // 说明是一个子节点
			Element child = parent.getChild(asChild);
			if (child == null) {
				child = new Element(asChild);
				parent.addContent(child);
			}
			child.setAttribute(attr, value);
		}
	}
	
	/** 获取类型转化类 */
	private synchronized static Transfer getTransfer(Class<? extends Transfer> clazz) throws InstantiationException, IllegalAccessException {
		Transfer transfer = _map.get(clazz);
		if (transfer == null) {
			transfer = clazz.newInstance();
			_map.put(clazz, transfer);
		}
		return transfer;
	} 
	
	public static void writeXml(Document doc, String encoding, String filename) {
		File file = new File(filename);
		writeXml(doc, encoding, file);
	}
	
	public static void writeXml(Document doc, String encoding, File file) {
		Format format = Format.getPrettyFormat();
		format.setEncoding(encoding);
		XMLOutputter xmlOut = new XMLOutputter(format);
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(file);
			xmlOut.output(doc, out);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
